#!/usr/bin/env python3

import re

import sublime
import sublime_plugin


def notify_nothing():
    sublime.status_message('Nothing to transpose')


def full_region(region):
    return (sublime.Region(region.begin(), region.begin() + 1)
            if region.empty() else region)


def perform_transposition(edit, view, trans, init_sel):
    " assumes trans is already reverse sorted sequence of regions"
    view.sel().subtract(init_sel)

    for i, (sel, substr) in enumerate(zip(trans, reversed([view.substr(s) for s in trans]))):
        view.replace(edit, sel, substr)
        if not i:
            if init_sel.empty():
                view.sel().add(sublime.Region(init_sel.a + 1))
            else:
                view.sel().add(init_sel)


def transpose_selections(edit, view, can_transpose_words=False):
    for sel in view.sel():
        word_sel = view.word(sel)
        word_extents = (wb, we) = (word_sel.begin(), word_sel.end())
        transpose_words = sel.end() in word_extents

        # " wora! arst"
        if transpose_words and can_transpose_words:
            if sel.end() == we:
                next = view.find(r'\w', word_sel.end())
                if next is None:
                    continue
                trans = [view.word(next), word_sel]
            else:
                if wb == 0:
                    continue
                for pt in range(wb - 1, -1, -1):
                    if re.match(r'\w', view.substr(pt)):
                        break
                trans = [word_sel, view.word(pt)]
        else:
            p1 = max(0, sel.begin() - 1)
            character_behind_region = sublime.Region(p1)
            # " a!a"
            trans = [full_region(sel), full_region(character_behind_region)]

        perform_transposition(edit, view, trans, sel)


def rotate_selections(edit, view):
    for sel in view.sel():
        if sel.empty():
            view.sel().add(view.word(sel))

    sels = list(reversed(view.sel()))

    strings = [view.substr(s) for s in sels]
    strings.append(strings.pop(0))

    for sel, substr in zip(sels, strings):
        view.replace(edit, sel, substr)


class Transpose(sublime_plugin.TextCommand):
    """
    - empty selection, cursor within a word: transpose characters
    - empty selection, cursor at the end of a word: transpose words
    - multiple selections, all empty: as above

    - multiple selections, at least one non-empty: rotate contents of selections
    (i.e., each selection takes on the contents of the selection before it)

    - single non-empty selection: do nothing

    """

    def run(self, edit, **kw):
        if not self.enabled():
            return notify_nothing()

        view = self.view
        sels = view.sel()
        nsels = len(sels)

        if nsels > 1 and view.has_non_empty_selection_region():
            rotate_selections(edit, view)
        else:
            transpose_selections(edit, view)

    def enabled(self):
        sels = self.view.sel()
        return not (len(sels) == 1 and not sels[0].empty())
